pip install pandas matplotlib seaborn
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
from matplotlib.gridspec import GridSpec
Очистка даних¶
В коді знизу я очищую таблицю з переписом 1778 року: прибираю службові рядки, перетворюю широку структуру в довгу, зберігаю інформацію про стать, статус у родині, вік і те, чи був хрещений. Результат - акуратна таблиця, з якою вже можна працювати в аналізі або візуалізаціях.
df_1778_raw = pd.read_csv("2.2_Дунаєць_1778.xlsx - База.csv", header=None, skiprows=3, dtype=str)
# задаємо назви колонок вручну, бо в csv їх немає
df_1778_raw.columns = [
"sheet", "person_id", "household_id", "source_house_id",
"man", "woman", "num_total", "num_source",
"first_name", "middle_name", "last_name", "family_status",
"category", "class", "social_status", "age",
"confessed", "not_confessed", "too_young"
]
# залишаємо тільки тих, у кого заповнене поле чоловік або жінка
df_1778 = df_1778_raw[df_1778_raw["man"].notna() | df_1778_raw["woman"].notna()].copy()
# тягнемо значення household_id, source_house_id і category вниз (там вони зазвичай лише в першому рядку)
df_1778["household_id"] = df_1778["household_id"].ffill()
df_1778["source_house_id"] = df_1778["source_house_id"].ffill()
df_1778["category"] = df_1778["category"].ffill()
# конвертуємо таблицю з wide-формату у long: один рядок — одна людина
people = []
for _, row in df_1778.iterrows():
for gender_col in ["man", "woman"]:
if pd.notna(row[gender_col]):
# переводимо мітку «х» у булеве значення
confessed_bool = None
if str(row["confessed"]).strip() == "х":
confessed_bool = True
elif str(row["not_confessed"]).strip() == "х" or str(row["too_young"]).strip() == "х":
confessed_bool = False
people.append({
"household_id": row["household_id"],
"gender": "чоловік" if gender_col == "man" else "жінка",
"first_name": row["first_name"],
"middle_name": row["middle_name"],
"last_name": row["last_name"],
"family_status": row["family_status"],
"category": row["category"],
"social_status": row["social_status"],
"age": pd.to_numeric(row["age"], errors="coerce"),
"confessed_bool": confessed_bool
})
df_1778_clean = pd.DataFrame(people)
df_1778_clean = df_1778_clean[df_1778_clean["first_name"] != "Імʼя"]
# заповнюємо прізвище вниз, бо воно часто лише в першій людині з родини
df_1778_clean["last_name"] = df_1778_clean["last_name"].ffill()
df_1778_clean = df_1778_clean.reset_index(drop=True)
df_1778_clean
| household_id | gender | first_name | middle_name | last_name | family_status | category | social_status | age | confessed_bool | |
|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1 | чоловік | Федор | Кондратьев | Лукашевич | господар | nuclear | духовные | 44.0 | True |
| 1 | 1 | жінка | Варвара | Симева | Лукашевич | жена | nuclear | духовные | 35.0 | True |
| 2 | 1 | жінка | Анна | NaN | Лукашевич | дочь | nuclear | духовные | 17.0 | True |
| 3 | 1 | чоловік | Тимофей | NaN | Лукашевич | сын | nuclear | духовные | 15.0 | True |
| 4 | 1 | чоловік | Пантелеймон | NaN | Лукашевич | сын | nuclear | духовные | 14.0 | True |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1596 | 104 | чоловік | Леонтий | Афанасиев | Кислая | двоюродный брат Александра | nuclear | бездворные | 25.0 | True |
| 1597 | 104 | жінка | Мотрона | Михайлова | Кислая | жена его | nuclear | бездворные | 22.0 | True |
| 1598 | 105 | чоловік | Герасим | Фомик | Мерник | господар | nuclear | бездворные | 43.0 | True |
| 1599 | 105 | жінка | Агафия | Максимова | Мерник | жена его | nuclear | бездворные | 42.0 | True |
| 1600 | 105 | чоловік | Лукьян | NaN | Мерник | NaN | nuclear | бездворные | 14.0 | True |
1601 rows × 10 columns
Тепер структура таблиці охоплює: ID людини, ID родини, стать, ім’я, по батькові, прізвище, сімейний статус, категорію родини, соціальний статус, вік та інформацію про те, чи була людина хрещена. Я намагалася доповнити колонку з по батькові - логіка полягала в тому, щоб на основі імені чоловіка в родині (наприклад, господаря чи дячка) автоматично формувати по батькові для дітей.
Однак через велику варіативність і несистемність у записах сімейного статусу виникало надто багато виняткових випадків, які важко було б коректно обробити в межах цього дослідження. До того ж, повне заповнення по батькові не дало б суттєвої додаткової інформації для подальшого аналізу, тому я вирішила зосередитись на інших, більш стабільних змінних.
У коді нижче я очистила таблицю з переписом населення 1897 року та привела її до зручного для аналізу формату. Зокрема, було видалено технічні колонки, які не містили аналітично цінної інформації (такі як посилання, ID рядків тощо), розбито повне ім’я на окремі поля — ім’я, по батькові та прізвище, нормалізовано стать (позначки “m” та “f” замінено на “чоловік” і “жінка” відповідно), а також перетворено деякі текстові поля на числові або булеві значення для подальшого аналізу. Т
df_1897_raw = pd.read_csv("2.1_Дунаєць.xlsx - База людей.csv")
df_1897 = df_1897_raw.rename(columns={
"Кто заполнял базу": "source",
"ID строки в базе": "row_id",
"List ID": "list_id",
"Link": "link",
"ID Домохозяйствао": "household_id",
"ID жилец": "person_id",
"ФИО": "full_name",
"Пол": "gender",
"Глава хозяйства и глава семьи": "head_status",
"Возраст": "age",
"Семейный статус": "family_status",
"Сословие, состояние или звание": "social_class",
"Здесь ли родился": "born_here",
"Место рождения": "birth_place",
"Здесь ли приписан": "registered_here",
"Здесь ли обыкновенно проживает": "lives_here",
"Отметка об отсуствии": "absence_note",
"Вероисповедание": "religion",
"Родной язык": "native_language",
"Умеет ли читать": "can_read",
"Обучение": "education",
"Профессия главное": "main_profession",
"Профессия вспомогательное": "secondary_profession",
"Положение по воинской повинности": "military_status",
"Примітки": "notes"
})
# прибираємо порожні рядки без імен — вони не несуть корисної інформації
df_1897 = df_1897[df_1897["full_name"].notna()]
# розбиваємо повне ім’я на прізвище, ім’я та по батькові
def split_full_name(name):
parts = str(name).split()
if len(parts) == 3:
return pd.Series({"last_name": parts[0], "first_name": parts[1], "middle_name": parts[2]})
elif len(parts) == 2:
return pd.Series({"last_name": parts[0], "first_name": parts[1], "middle_name": None})
else:
return pd.Series({"last_name": None, "first_name": None, "middle_name": None})
df_1897 = df_1897.join(df_1897["full_name"].apply(split_full_name))
# видаляємо технічні колонки та колонку з повним ім’ям — тепер вони нам не потрібні
df_1897 = df_1897.drop(columns=["source", "row_id", "list_id", "link", "full_name", "person_id"])
# замінюємо "m"/"f" на "чоловік"/"жінка" для зручності подальшого аналізу
df_1897["gender"] = df_1897["gender"].map({"m": "чоловік", "f": "жінка"}).fillna(df_1897["gender"])
df_1897["age"] = pd.to_numeric(df_1897["age"], errors="coerce")
df_1897["household_id"] = pd.to_numeric(df_1897["household_id"], errors="coerce")
df_1897["can_read"] = df_1897["can_read"].map({1: True, 0: False, "1": True, "0": False})
df_1897 = df_1897.reset_index(drop=True)
df_1897
| household_id | gender | head_status | age | family_status | social_class | born_here | birth_place | registered_here | lives_here | ... | native_language | can_read | education | main_profession | secondary_profession | military_status | notes | last_name | first_name | middle_name | |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 0 | 1.0 | чоловік | husband | 32.0 | married | cossack | 1.0 | Dunaec | 1 | 1 | ... | ukr | True | rural school | farmer | NaN | Nizhnij chin zapasu | NaN | Krasovskij | Pantelejmon | Ivanovich |
| 1 | 1.0 | жінка | wife | 28.0 | married | cossack | 0.0 | Chernigovskaya gub., Glukhovskij pov., Kholopk... | 1 | 1 | ... | ukr | False | NaN | farmer with husband | NaN | NaN | NaN | Krasovskaya | Stefanida | Artemieva |
| 2 | 1.0 | жінка | daughter | 5.0 | unmarried | cossack | 1.0 | Dunaec | 1 | 1 | ... | ukr | False | NaN | with father | NaN | NaN | NaN | Krasovskaya | Anna | Pantelejmonova |
| 3 | 1.0 | чоловік | son | 1.0 | unmarried | cossack | 1.0 | Dunaec | 1 | 1 | ... | ukrm | False | NaN | with father | NaN | NaN | NaN | Krasovskij | Stefan | Pantelejmonov |
| 4 | 1.0 | чоловік | father | 70.0 | vidov | cossack | 1.0 | Dunaec | 1 | 1 | ... | ukr | False | NaN | with son | NaN | NaN | Slep na oba hlaza 10 let nazad | Krasovskij | Ivan | Kondratov |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 1540 | 261.0 | жінка | wife | 30.0 | married | peasant-owner | 0.0 | Chernigovskaya gub., Glukhovskij pov., m. Glukhov | 0 / Chernigovskaya gub., Glukhovskij pov., s. ... | 1 | ... | ukr | False | NaN | with husband | NaN | NaN | NaN | Gricunova | Motrona | Nikolaeva |
| 1541 | 261.0 | жінка | daughter | 9.0 | unmarried | peasant-owner | 0.0 | Chernigovskaya gub., Glukhovskij pov., Tuligol... | 0 / Chernigovskaya gub., Glukhovskij pov., s. ... | 1 | ... | ukr | False | NaN | with father | NaN | NaN | NaN | Gricunova | Elena | Sergeeva |
| 1542 | 261.0 | чоловік | son | 7.0 | unmarried | peasant-owner | 0.0 | Chernigovskaya gub., Glukhovskij pov., m. Glukhov | 0 / Chernigovskaya gub., Glukhovskij pov., s. ... | 1 | ... | ukr | False | NaN | with father | NaN | NaN | NaN | Gricunov | Nikolaj | Sergeev |
| 1543 | 261.0 | чоловік | son | 5.0 | unmarried | peasant-owner | 0.0 | Chernigovskaya gub., Glukhovskij pov., s. Godu... | 0 / Chernigovskaya gub., Glukhovskij pov., s. ... | 1 | ... | ukr | False | NaN | with father | NaN | NaN | NaN | Gricunov | Aleksandr | Sergeev |
| 1544 | 261.0 | чоловік | son | 3.0 | unmarried | peasant-owner | 0.0 | Chernigovskaya gub., Glukhovskij pov., Tuligol... | 0 / Chernigovskaya gub., Glukhovskij pov., s. ... | 1 | ... | ukr | False | NaN | with father | NaN | NaN | NaN | Gricunov | Mikhail | Sergeev |
1545 rows × 22 columns
Агрегація даних та візуалізація¶
У цій частині я проаналізувала вікову структуру населення на основі переписів. Зокрема, було побудовано дашборд із кількох панелей, що включає:
- Порівняльне вікове розподілення у вигляді накладених гістограм для обох років.
- Середній вік населення за статтю у вигляді стовпчикової діаграми.
- Статево-віковий розподіл у форматі 100% stacked area chart.
- Дві демографічні піраміди для 1778 та 1897 років, які дозволяють побачити гендерний баланс у різних вікових групах.
sns.set(style="darkgrid")
main_color = "#4C72B0"
alt_color = "#DD8452"
# чистка даних для графіків
df_1778_clean = df_1778_clean[df_1778_clean["age"].notna()]
df_1897 = df_1897[df_1897["age"].notna()]
# визначення вікових груп
bins = list(range(0, 101, 10))
labels = [f"{i}-{i+9}" for i in bins[:-1]]
df_1778_clean["age_group"] = pd.cut(df_1778_clean["age"], bins=bins, labels=labels, right=False)
df_1897["age_group"] = pd.cut(df_1897["age"], bins=bins, labels=labels, right=False)
fig = plt.figure(figsize=(20, 16))
gs = GridSpec(3, 3, figure=fig)
fig.suptitle("Вікова структура населення: порівняння 1778 vs 1897", fontsize=18)
# 1. гістограма
ax1 = fig.add_subplot(gs[0, 0:2])
sns.histplot(df_1778_clean["age"], bins=range(0, 101, 5), ax=ax1, color=main_color, label='1778', alpha=0.6)
sns.histplot(df_1897["age"], bins=range(0, 101, 5), ax=ax1, color=alt_color, label='1897', alpha=0.6)
ax1.set_title("Порівняльне вікове розподілення")
ax1.set_xlabel("Вік")
ax1.set_ylabel("Кількість людей")
ax1.legend()
# 2. середній вік
ax2 = fig.add_subplot(gs[0, 2])
avg_1778 = df_1778_clean.groupby("gender")["age"].mean()
avg_1897 = df_1897.groupby("gender")["age"].mean()
avg_df = pd.DataFrame({"1778": avg_1778, "1897": avg_1897}).T
bars = avg_df.plot(kind='bar', ax=ax2, color=[main_color, alt_color])
ax2.set_title("Середній вік за статтю")
ax2.set_ylabel("Середній вік")
ax2.legend(title="Стать")
for p in bars.patches:
value = round(p.get_height(), 1)
ax2.annotate(f'{value}', (p.get_x() + p.get_width() / 2, p.get_height()),
ha='center', va='bottom', fontsize=10)
# 3. статево-віковий розподіл 1778
ax3 = fig.add_subplot(gs[1, 0])
gender_age_1778 = df_1778_clean.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
gender_age_1778_pct = gender_age_1778.div(gender_age_1778.sum(axis=1), axis=0)
gender_age_1778_pct = gender_age_1778_pct[["чоловік", "жінка"]]
ax3.stackplot(gender_age_1778_pct.index.astype(str),
gender_age_1778_pct["чоловік"],
gender_age_1778_pct["жінка"],
labels=["чоловік", "жінка"],
colors=[main_color, alt_color])
ax3.set_title("Статево-віковий розподіл (1778)")
ax3.set_ylabel("Частка")
ax3.legend()
ax3.tick_params(axis='x', rotation=45)
# 4. статево-віковий розподіл 1897
ax4 = fig.add_subplot(gs[2, 0])
gender_age_1897 = df_1897.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
gender_age_1897_pct = gender_age_1897.div(gender_age_1897.sum(axis=1), axis=0)
gender_age_1897_pct = gender_age_1897_pct[["чоловік", "жінка"]]
ax4.stackplot(gender_age_1897_pct.index.astype(str),
gender_age_1897_pct["чоловік"],
gender_age_1897_pct["жінка"],
labels=["чоловік", "жінка"],
colors=[main_color, alt_color])
ax4.set_title("Статево-віковий розподіл (1897)")
ax4.set_ylabel("Частка")
ax4.legend()
ax4.tick_params(axis='x', rotation=45)
# 5. піраміда 1778
ax5 = fig.add_subplot(gs[1:, 1])
pyramid_1778 = df_1778_clean.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
pyramid_1778["чоловік"] *= -1
ax5.barh(pyramid_1778.index.astype(str), pyramid_1778["чоловік"], color=main_color, label="чоловік")
ax5.barh(pyramid_1778.index.astype(str), pyramid_1778["жінка"], color=alt_color, label="жінка")
ax5.set_title("Піраміда населення 1778")
ax5.set_xlabel("Кількість")
ax5.set_ylabel("Вікова група")
ax5.legend()
# 6. піраміда 1897
ax6 = fig.add_subplot(gs[1:, 2])
pyramid_1897 = df_1897.groupby(["age_group", "gender"], observed=False).size().unstack(fill_value=0)
pyramid_1897["чоловік"] *= -1
ax6.barh(pyramid_1897.index.astype(str), pyramid_1897["чоловік"], color=main_color, label="чоловік")
ax6.barh(pyramid_1897.index.astype(str), pyramid_1897["жінка"], color=alt_color, label="жінка")
ax6.set_title("Піраміда населення 1897")
ax6.set_xlabel("Кількість")
ax6.set_ylabel("Вікова група")
ax6.legend()
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
Ключові інсайти зі зрівняльного аналізу по віку:¶
Зростання середнього віку за статтю У 1778 році середній вік був приблизно 23.5 років як у чоловіків, так і у жінок. У 1897 році ситуація значно покращилась: середній вік виріс до 25.2 у чоловіків і 27.7 у жінок. Це свідчить про покращення загальних умов життя та зменшення смертності.
Молоде населення домінує у 1778 році За гістограмою вікового розподілу видно, що у 1778 році значна частина населення була у віковій категорії 0-20 років. Загалом у цьому році спостерігається вища концентрація молоді порівняно з 1897.
Зростання кількості людей старшого віку у 1897 році У 1897 році більше людей доживають до похилого віку, зокрема у групах 60+, 70+ років. У 1778 таких майже немає, що видно як з гістограми, так і з пірамід.
Демографічна піраміда 1897 року більш збалансована Піраміди показують, що у 1897 році розподіл населення за віком є рівномірнішим і демонструє більш «стабільне» суспільство. У 1778 піраміда різко звужується з віком.
У цій частині дослідження я проаналізувала склад родин. Було побудовано дашборд, що включає:
- Гістограму розподілу розмірів родин у двох роках - показує, які родини були найпоширенішими.
- Стовпчикову діаграму середнього розміру родини для кожного року.
- Boxplot-діаграми кількості чоловіків та жінок у родинах.
sns.set(style="darkgrid")
main_color = "#4C72B0"
alt_color = "#DD8452"
df_1778_clean['household_id'] = df_1778_clean['household_id'].astype(int)
df_1897['household_id'] = df_1897['household_id'].astype(int)
# групування по родинам
families_1778 = df_1778_clean.groupby("household_id").agg(
family_size=("gender", "count"),
num_men=("gender", lambda x: (x == "чоловік").sum()),
num_women=("gender", lambda x: (x == "жінка").sum())
).reset_index()
families_1778["year"] = 1778
families_1897 = df_1897.groupby("household_id").agg(
family_size=("gender", "count"),
num_men=("gender", lambda x: (x == "чоловік").sum()),
num_women=("gender", lambda x: (x == "жінка").sum())
).reset_index()
families_1897["year"] = 1897
families_all = pd.concat([families_1778, families_1897], ignore_index=True)
fig = plt.figure(figsize=(18, 10))
gs = GridSpec(2, 2, figure=fig)
fig.suptitle("Аналіз родин у 1778 і 1897 роках", fontsize=18)
# 1. розподіл розмірів родин
ax1 = fig.add_subplot(gs[0, 0])
bins_range = range(1, families_all["family_size"].max() + 2)
sns.histplot(
data=families_all,
x="family_size",
hue="year",
multiple="dodge",
bins=bins_range,
palette=[main_color, alt_color],
ax=ax1
)
ax1.set_title("Розподіл розмірів родин")
ax1.set_xlabel("Кількість людей у родині")
ax1.set_ylabel("Кількість родин")
# 2. середній розмір родини
ax2 = fig.add_subplot(gs[0, 1])
avg_sizes = families_all.groupby("year")["family_size"].mean().reset_index()
sns.barplot(
data=avg_sizes,
x="year",
y="family_size",
hue="year",
palette=[main_color, alt_color],
ax=ax2,
legend=False
)
ax2.set_title("Середній розмір родини")
ax2.set_xlabel("Рік")
ax2.set_ylabel("Середній розмір")
# 3. кількість чоловіків
ax3 = fig.add_subplot(gs[1, 0])
sns.boxplot(
data=families_all,
x="year",
y="num_men",
hue="year",
palette=[main_color, alt_color],
ax=ax3,
legend=False
)
ax3.set_title("Кількість чоловіків у родинах")
ax3.set_xlabel("Рік")
ax3.set_ylabel("Осіб")
# 4. кількість жінок
ax4 = fig.add_subplot(gs[1, 1])
sns.boxplot(
data=families_all,
x="year",
y="num_women",
hue="year",
palette=[main_color, alt_color],
ax=ax4,
legend=False
)
ax4.set_title("Кількість жінок у родинах")
ax4.set_xlabel("Рік")
ax4.set_ylabel("Осіб")
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
Основні інсайти з графіків¶
Розмір родин значно зменшився у 1897 році. Якщо в 1778 році зустрічаються родини з чисельністю понад 30 осіб (і навіть вище 70!), то в 1897 році більшість родин обмежується 5–10 людьми.
Середній розмір родини зменшився вдвічі — із приблизно 15 осіб у 1778 до менш як 7 у 1897. Це може свідчити про перехід від розширених до нуклеарних родин.
Розподіл за статтю всередині родин також став компактнішим. Boxplot показує, що у 1778 році було значно більше чоловіків та жінок у родині (медіани — 6–8 осіб), тоді як у 1897 — 3–4.
У 1778 році були надзвичайно великі господарства, що видно по численним "вусам" та викидам на boxplot'ах. У 1897 таких випадків значно менше.
Зменшення кількості великих родин може бути пов’язане як із демографічними факторами (війни, хвороби), так і з соціальними змінами (поділ земель, нові економічні моделі ведення господарства).
У цій частині дослідження я проаналізувала шлюбний стан населення за переписом 1897 року.
На жаль, дані за 1778 рік є надто фрагментарними та архаїчними, тож побудувати повноцінну візуалізацію для цього періоду не вдалося. Уся представлена аналітика базується на даних за 1897 рік.
Що зображено на дашборді?
Карта теплових значень: кількість людей за віком та шлюбним статусом Показує, у якому віці найчастіше перебувають у статусі "одружений", "неодружений" або "вдівець/вдова".
Кругова діаграма: розподіл шлюбних статусів усього населення Відображає загальні частки married / unmarried / widowed у структурі населення.
Boxplot: розподіл віку за шлюбним статусом Дає змогу побачити, у якому віці найчастіше вступають у шлюб, залишаються неодруженими або стають вдівцями.
Карта теплових часток: частка кожного шлюбного статусу в кожній віковій групі Дозволяє виявити, які вікові групи найчастіше відповідають тому чи іншому статусу.
sns.set(style="darkgrid")
mako_palette = sns.color_palette("mako", n_colors=3)
mako_cmap = sns.color_palette("mako", as_cmap=True)
heatmap_data = df_1897.groupby(['age', 'family_status']).size().unstack(fill_value=0)
normalized = heatmap_data.div(heatmap_data.sum(axis=1), axis=0)
pie_data = df_1897['family_status'].value_counts()
fig = plt.figure(figsize=(20, 12))
gs = GridSpec(2, 2, figure=fig)
fig.suptitle("Аналіз шлюбного статусу населення у 1897 році", fontsize=20)
# абсолютна кількість хітмап
ax1 = fig.add_subplot(gs[0, 0])
sns.heatmap(heatmap_data, cmap=mako_cmap, linewidths=0.3, ax=ax1)
ax1.set_title("Кількість людей за віком та шлюбним статусом")
ax1.set_xlabel("Шлюбний статус")
ax1.set_ylabel("Вік")
# розподіл статусів
ax2 = fig.add_subplot(gs[0, 1])
ax2.pie(pie_data, labels=pie_data.index, autopct='%1.1f%%', startangle=140, colors=mako_palette)
ax2.set_title("Розподіл шлюбного статусу")
# вік за статусом
ax3 = fig.add_subplot(gs[1, 0])
sns.boxplot(data=df_1897, x='family_status', y='age', hue='family_status', palette=mako_palette, ax=ax3, legend=False)
ax3.set_title("Розподіл віку за шлюбним статусом")
ax3.set_xlabel("Шлюбний статус")
ax3.set_ylabel("Вік")
ax3.tick_params(axis='x', rotation=15)
# частка статусів у кожному віці ще раз хітмап
ax4 = fig.add_subplot(gs[1, 1])
sns.heatmap(normalized, cmap=mako_cmap, linewidths=0.3, ax=ax4)
ax4.set_title("Частка шлюбних статусів у кожній віковій групі")
ax4.set_xlabel("Шлюбний статус")
ax4.set_ylabel("Вік")
plt.tight_layout(rect=[0, 0, 1, 0.95])
plt.show()
Інсайти:¶
Найбільша частка населення — неодружені Згідно з круговою діаграмою:
51.1%населення були неодруженими42.0%- у шлюбі- лише
6.8%- вдівці/вдови Це може свідчити про загальну молодість населення або високий поріг вступу в шлюб.
Шлюб найпоширеніший у віковому діапазоні 25–50 років За абсолютною heatmap видно, що найбільше людей у статусі married припадає на середній вік - приблизно від 25 до 50 років. Саме цей вік був найактивнішим щодо укладення шлюбів.
Вдівство переважає у старших вікових групах (60+) Відповідно до heatmap та boxplot, статус vidov (вдова/вдівець) різко зростає після 60 років. Це свідчить про високу смертність одного з подружжя в старшому віці.
Неодружені - переважно молодь до 30 років Розподіл віку показує, що unmarried мають найнижчий медіанний вік. Це логічно — молодь ще не вступила в шлюб або перебуває в «активному шлюбному віці».